﻿using System;
using System.Collections.Generic;
using System.Linq;
using Oxage.CNC.Models;

namespace Oxage.Simulator
{
	[InlineBinding(typeof(IBezierInline))]
	public class BezierStepMaker : IStepMaker
	{
		#region Protected methods
		//Bezier curves: http://en.wikipedia.org/wiki/B%C3%A9zier_curve
		protected NPoint GetBezierCurvePoint(List<NPoint> points, double t)
		{
			int count = points.Count;
			if (count == 0)
			{
				return new NPoint();
			}
			else if (count == 1)
			{
				return points[0];
			}
			else if (t <= 0)
			{
				return points.FirstOrDefault();
			}
			else if (t >= 1)
			{
				return points.LastOrDefault();
			}

			NPoint[] input = points.ToArray();
			NPoint[] temp = new NPoint[count - 1];

			while (input.Length >= 2)
			{
				for (int i = 0; i < count - 1; i++)
				{
					var p1 = input[i];
					var p2 = input[i + 1];
					temp[i] = new NPoint(p1.X + (p2.X - p1.X) * t, p1.Y + (p2.Y - p1.Y) * t);
				}

				input = temp;
				count = input.Length;

				if (count >= 2)
				{
					temp = new NPoint[count - 1];
				}
			}

			return temp[0];
		}
		#endregion

		#region Public methods
		public void FollowBezierPath(IStepDriver driver, NPoint startPoint, NPoint controlPoint1, NPoint controlPoint2, NPoint endPoint)
		{
			var aux = new MoveStepMaker();

			//Parameter t goes from 0 to 1
			for (double t = 0.0; t <= 1.0; t += 0.01)
			{
				var points = new List<NPoint>
				{
					startPoint,
					controlPoint1,
					controlPoint2,
					endPoint
				};

				var p = GetBezierCurvePoint(points, t);
				aux.MoveAbsolute(driver, p);

				//TODO: Implement native steps
				//driver.Step(result);
			}
		}
		#endregion

		#region IStepMaker Members
		public void Run(IStepDriver driver, object data)
		{
			if (driver == null)
			{
				throw new ArgumentNullException("driver");
			}

			var inline = data as IBezierInline;
			if (inline == null)
			{
				throw new ArgumentException("Expected IBezierInline");
			}

			FollowBezierPath(driver, new NPoint(inline.StartPoint), new NPoint(inline.ControlPoint1), new NPoint(inline.ControlPoint2), new NPoint(inline.EndPoint));
		}
		#endregion
	}
}
